A área de governância possui um sistema de registro de chamados de incidêntes. Esses sistema é responsável por gerenciar todo o ciclo de vida dos chamados. Apesar disso o sistema não possui um processo para analisar os chamados encerrados e observar padrões nos dados que possam trazer insights para melhorar a resolução dos top incidêntes.
A proposta é analisar a descrição dos chamados e encontrar um padrão para identificar macro tópicos de problemas. Um grande desafio é a necessidade de processamento de linguagem natural, haja vista, a grande quantidade de chamados e a forma como as descrições estão armazenadas. Esse estudo visa analisar a viabilidade para esse tipo de problema e que pode ser escalado para outras análises textuais de tópicos.
A principal biblioteca usada é a 'texthero' para tratamento textual.
import pandas as pd
import texthero as hero
import matplotlib.pyplot as plt
from texthero import stopwords
import wordcloud
import numpy as np
from math import sqrt
from sklearn.cluster import KMeans
tickets_db = pd.read_csv('file_db.csv')
O estudo de viabilidade está considerando descrições da categoria 2 de Empréstimo Pessoa Física, mais especificamente os registros de erros funcionais.
epf_filter = tickets_db['Categoria_2'] == 'EPF - Emprestimo Pessoa Fisica'
erro_filter = tickets_db['Categoria_3'] == 'Erro Funcional'
indisponivel_filter = tickets_db['Categoria_3'] == 'Sistema indisponivel'
aplicativo_df = tickets_db[epf_filter & erro_filter]
aplicativo_df.shape
o hero.clean() executa sete funções quando você passa uma série de pandas. Essas sete funções são:
problems_content = aplicativo_df[["descricao_problema"]]
problems_content['clean_content'] = hero.clean(problems_content['descricao_problema'])
problems_content
#tirando palavras específicas
default_stopwords = stopwords.DEFAULT
custom_stopwords = default_stopwords.union(set(['os','ao','nao','noite','obrigado', 'att'
'boa','bom','em','foi','na','se','com','esta',
'dia','tarde','no','do','de','da','o','a','favor',
'que','e','gentileza','bom dia','favor', 'boa noite','para','por']))
clean_text = hero.remove_stopwords(problems_content['clean_content'], custom_stopwords)
clean_text = hero.preprocessing.stem(clean_text,language='portuguese',stem='porter')
clean_text
NUM_TOP_WORDS = 20
top_20 = hero.visualization.top_words(clean_text).head(NUM_TOP_WORDS)
# Draw the bar chart
top_20.plot.bar(rot=90, title="Top 20 words");
plt.show(block=True);
TF-IDF é uma medida estatística que avalia a relevância de uma palavra para um documento em uma coleção de documentos. Isso é feito multiplicando duas métricas: quantas vezes uma palavra aparece em um documento e a frequência inversa da palavra em um conjunto de documentos.
Ele tem muitos usos, o mais importante na análise automatizada de texto, e é muito útil para pontuar palavras em algoritmos de aprendizado de máquina para Processamento de Linguagem Natural (PNL).
column_names = ["content","tfidf", "kmeans_labels"]
descricao_problemas_df = pd.DataFrame (columns = column_names)
descricao_problemas_df["content"] = clean_text
# convert them into tf-idf features.
descricao_problemas_df['tfidf'] = (
descricao_problemas_df['content']
.pipe(hero.tfidf)
)
# perform clustering algorithm by using kmeans()
descricao_problemas_df['kmeans_labels'] = (
descricao_problemas_df['tfidf']
.pipe(hero.kmeans, n_clusters=5)
.astype(str)
)
descricao_problemas_df.head()
descricao_problemas_df['kmeans_labels'].value_counts()
O objetivo do PCA é encontrar um meio de condensar a informação contida em várias variáveis originais em um conjunto menor de variáveis estatísticas (componentes) com uma perda mínima de informação.
Verifica-se a formação de 5 clusters de descrição de problemas. É necessário ainda a análise de cada grupo para identificar o tópico de cada um.
#perform pca
descricao_problemas_df['pca'] = descricao_problemas_df['tfidf'].pipe(hero.pca)
#show scatterplot
hero.scatterplot(descricao_problemas_df, 'pca', color="kmeans_labels", title="Descriçao de Problemas por Grupo")
O ponto que indica o equilíbrio entre maior homogeneidade dentro do cluster e a maior diferença entre clusters, é o ponto da curva mais distante de uma reta traçada entre os pontos P0 = a0 e P1 = a18.

data = np.array(descricao_problemas_df['pca'].to_list())
def calculate_wcss(data):
wcss = []
for n in range(2, 21):
kmeans = KMeans(n_clusters=n)
kmeans.fit(X=data)
wcss.append(kmeans.inertia_)
return wcss
def optimal_number_of_clusters(wcss):
x1, y1 = 2, wcss[0]
x2, y2 = 20, wcss[len(wcss)-1]
distances = []
for i in range(len(wcss)):
x0 = i+2
y0 = wcss[i]
numerator = abs((y2-y1)*x0 - (x2-x1)*y0 + x2*y1 - y2*x1)
denominator = sqrt((y2 - y1)**2 + (x2 - x1)**2)
distances.append(numerator/denominator)
return distances.index(max(distances)) + 2
df = data
sum_of_squares = calculate_wcss(df)
# calculando a quantidade ótima de clusters
n = optimal_number_of_clusters(sum_of_squares)
x1, x2 = 2, 20
intervalo = range(x1,x2+1)
plt.figure(figsize=(15,5))
plt.title('Método do cotovelo')
plt.xlabel('Quantidade de clusters')
plt.ylabel('Soma dos quadrados intra-clusters')
plt.grid()
plt.xticks(intervalo)
plt.plot(intervalo, sum_of_squares) # pontos laranjas
plt.plot(intervalo, sum_of_squares, '.') # linha azul
y2 = sum_of_squares[len(sum_of_squares)-1]
y1 = sum_of_squares[0]
plt.plot([x2, x1], [y2,y1]) # linha verde
for x,y in zip(intervalo,sum_of_squares): # colocando nome nos pontos
label = "a{}".format(x-2)
plt.annotate(label,
(x,y),
textcoords="offset points",
xytext=(-5,-10),
ha='right')
plt.show()
print('Número ideal de clusters: ' + str(n))
kmeans_filter = descricao_problemas_df['kmeans_labels'] == '2'
pd.DataFrame(descricao_problemas_df[kmeans_filter]['content'].head(20))
hero.wordcloud(descricao_problemas_df[kmeans_filter]['content'], max_words=10) #, width=100, height=100
Analisando o aplicativo 'EPF - Emprestimo Pessoa Física' e olhando os erros funcionais, verificou-se o agrupamento de diferentes informações. Essas informações podem guiar o rumo das entrevistas com as áreas, como:
A proposta da separação por grupos(clusters) é identificar similaridades e assim identificar tópicos recorrentes de problemas enfrentados pelas áreas.
Esse estudo mostrou a viabilidade de prosseguir com essa abordagem e encontrar mais temas que possam auxiliar nas entrevistas.
from top2vec import Top2Vec
descr_problem_list = descricao_problemas_df['content'].to_list()
#model = Top2Vec(documents, embedding_model='universal-sentence-encoder')
model = Top2Vec(documents=descr_problem_list, speed="learn", workers=8)
model.get_num_topics()
topic_words, word_scores, topic_nums = model.get_topics(2)
for topic in topic_nums:
model.generate_topic_wordcloud(topic)
Top2Vec apresentou resultados confusos e não conclusivos. Carece de análise mais profunda para entender esse comportamento.